/*
 * @Descripttion : 颜色拾取器
 * @Author       : QianFox Team
 * @Date         : 2022-04-11 17:10:22
 * @LastEditors  : QianFox Team
 * @LastEditTime : 2024-03-05 15:33:00
 */

import { Popper } from './popper';
import { _checkRGBA, _checkRGB } from './common/verification';

function Color() {
    Popper.call(this);
    this.HANDLE = '.foxui-color-handle:not(.is-disabled)';
    this.MENU = '.foxui-color-menu';
    this.CONTAINER = '.foxui-color';
    this.SVPANEL = '.foxui-color-svpanel';
    this.SVPANEL_CURSOR = '.foxui-color-svpanel__cursor';
    this.HSLIDER = '.foxui-color-hslider';
    this.HSLIDER_THUMB = '.foxui-color-hslider__thumb';
    this.ASLIDER = '.foxui-color-aslider';
    this.ASLIDER_THUMB = '.foxui-color-aslider__thumb';
    this.ASLIDER_BAR = '.foxui-color-aslider__bar';
    this.BOTTOM_BTNS = '.foxui-color-bottom__btns';
    this.CLEAR = '.foxui-color-bottom__btns .clear';
    this.CONFIRM = '.foxui-color-bottom__btns .confirm';
    this.COLOR_SHOW = '.foxui-color-show';

    // 拖动面板(动态)
    this.markP = ''; // x:只能横向移；y:只能纵向移动；xy:都能移动
    this.panelWidth = 0; // 拖动面板的宽
    this.panelHeight = 0; // 拖动面板的高
    this.panelP = null; // 拖动面板的位置 {left:0, top:0}
    this.$mark = null; // 指示点的 jQuery 节点
    // 颜色数据
    this.isAlpha = false;
    this.panelXY = [255, 0];
    this.hsliderY = 0;
    this.asliderX = 255;
    this.alpha = 1;
    this.hsv = [0, 1, 1];
    this.rgb = [255, 0, 0];
    this.svFieldHsv = [0, 1, 1];
    this.svFieldRgb = [255, 0, 0];
}

Object.setPrototypeOf(Color.prototype, Popper.prototype);

Color.prototype._init = function () {
    this._bindToggle();
    this._bindMenuStopPropagation();
    this._bindDrag();
    this._bindConfirm();
    this._bindClear();
};

/**
 * @description: 阻止点击 menu 面板冒泡
 * @param {*}
 * @return {*}
 * @Date: 2022-04-12 21:20:54
 */
Color.prototype._bindMenuStopPropagation = function () {
    $(document).on('click', this.MENU, function (event) {
        event.stopPropagation();
    });
};

Color.prototype._bindToggle = function () {
    let that = this;
    $(document).on('click', this.HANDLE, function (event) {
        event.stopPropagation();
        event.preventDefault();

        let $handle = $(this),
            $menu = $handle.siblings(that.MENU),
            // $colorShow = $handle.find(that.COLOR_SHOW),
            isActive = $handle.is('.is-active'),
            isAlpha = $handle.is('.is-alpha'),
            len = $menu.length;

        // 保存isAlpha
        that.isAlpha = isAlpha;

        // 弹出框显示/隐藏
        if (isActive) {
            $handle.removeClass('is-active');
            $menu.slideUp('fast');
        } else {
            //初始化数据
            // let initColor = $colorShow.css('background-color'),
            let initColor = $handle.find('input').val(),
                isEffective = false,
                colorArr = [];
            // 判断是 rgba / rgb
            if (isAlpha) {
                let colorStr = initColor.slice(5, initColor.length - 1);
                colorArr = colorStr.split(',');
                isEffective = _checkRGBA(initColor);
            } else {
                let colorStr = initColor.slice(4, initColor.length - 1);
                colorArr = colorStr.split(',');
                isEffective = _checkRGB(initColor);
            }
            // 颜色值是否有效
            if (isEffective) {
                let r = parseInt(colorArr[0]),
                    g = parseInt(colorArr[1]),
                    b = parseInt(colorArr[2]),
                    a = parseFloat(colorArr[3]),
                    hsv = that._rgb2hsv(r, g, b);
                that.panelXY = [parseInt(hsv[1] * 255), parseInt((1 - hsv[2]) * 255)];
                that.hsliderY = parseInt((hsv[0] / 360) * 255);
                that.asliderX = a || a == 0 ? parseInt(a * 255) : 255;
                that.alpha = a;
                that.hsv = hsv;
                that.rgb = [r, g, b];
                that.svFieldHsv = [hsv[0], 1, 1];
                that.svFieldRgb = that._hsv2rgb(hsv[0], 1, 1);
            } else {
                // 无效 重置
                that.panelXY = [255, 0];
                that.hsliderY = 0;
                that.asliderX = 255;
                that.alpha = 1;
                that.hsv = [0, 1, 1];
                that.rgb = [255, 0, 0];
                that.svFieldHsv = [0, 1, 1];
                that.svFieldRgb = [255, 0, 0];
            }

            // 追加节点/替换节点
            if (len > 0) {
                $menu.empty().append(that._panelHtml());
            } else {
                $handle.closest(that.CONTAINER).append(`<div class="foxui-color-menu">${that._panelHtml()}</div>`);
            }
            // 关闭所有的 menus
            that._clearMenus();
            // 设置弹出面板的位置(需放在显示前，否则会跳动);
            that._setPosition(this);
            // 显示
            $handle.addClass('is-active');
            // 不能用之前的 $menu，追加之前的，这个对象不存在
            $handle.siblings(that.MENU).slideDown('fast');
            // 如有颜色的，填入弹框底部输入框
            $handle
                .closest(that.CONTAINER)
                .find(that.BOTTOM_BTNS)
                .find('input')
                .val(initColor || '');
        }
    });
};

Color.prototype._panelHtml = function () {
    return [
        '<div class="foxui-color-menu__inner">',
        '<div class="foxui-color-menu__main-wrapper">',
        this._svpanelHtml(),
        this._hsliderHtml(),
        '</div>',
        this.isAlpha ? this._asliderHtml() : '',
        this._btnsHtml(),
        '</div>',
    ].join('');
};

Color.prototype._hsliderHtml = function () {
    return ['<div class="foxui-color-hslider">', '<div class="foxui-color-hslider__bar"></div>', `<div class="foxui-color-hslider__thumb" style="top:${this.hsliderY}px"></div>`, '</div>'].join('');
};

Color.prototype._svpanelHtml = function () {
    return [
        `<div class="foxui-color-svpanel" style="background-color: rgb(${this.svFieldRgb.join(',')})">`,
        '<div class="foxui-color-svpanel__white"></div>',
        '<div class="foxui-color-svpanel__black"></div>',
        `<div class="foxui-color-svpanel__cursor" style="left:${this.panelXY[0]}px;top:${this.panelXY[1]}px"><span></span></div>`,
        '</div>',
    ].join('');
};

Color.prototype._asliderHtml = function () {
    return [
        '<div class="foxui-color-aslider">',
        `<div class="foxui-color-aslider__bar" style="background: linear-gradient(to right, rgba(${this.svFieldRgb.join()}, 0) 0%, rgb(${this.svFieldRgb.join()}) 100%);"></div>`,
        `<div class="foxui-color-aslider__thumb" style="left:${this.asliderX}px"></div>`,
        '</div>',
    ].join('');
};

Color.prototype._btnsHtml = function () {
    return [
        '<div class="foxui-color-bottom__btns">',
        '<div class="foxui-color-botton__input">',
        '<input class="foxui-size-mini" value/>',
        '</div>',
        '<div>',
        '<button class="foxui-text-primary foxui-size-mini clear">清空</button>',
        '<button class="foxui-plain foxui-size-mini confirm">确定</button>',
        '</div>',
        '</div>',
    ].join('');
};

/**
 * @description: 绑定确定
 * @param {*}
 * @return {*}
 * @Date: 2022-04-13 08:17:30
 */
Color.prototype._bindConfirm = function () {
    let that = this;
    $(document).on('click', this.CONFIRM, function () {
        let $this = $(this),
            $container = $this.closest(that.CONTAINER),
            $binput = $this.closest(that.BOTTOM_BTNS).find('input'),
            $input = $container.find(that.HANDLE).find('input'),
            $colorShow = $container.find(that.COLOR_SHOW),
            $handle = $container.find(that.HANDLE),
            $close = $handle.find('i'),
            $menu = $container.find(that.MENU),
            isAlpha = $handle.is('.is-alpha'),
            cValue = $binput.val();

        let isEffective = false;
        // 判断是 rgba / rgb
        if (isAlpha) {
            isEffective = _checkRGBA(cValue);
        } else {
            isEffective = _checkRGB(cValue);
        }
        // 如果值有效
        if (isEffective) {
            // 输入框中填入值
            $input.val(cValue);
            // 设置展示的颜色
            $colorShow.css('background-color', cValue);
            // 设置 展示框的 关闭 icon
            $close.css('display', 'none');
        }
        // 关闭弹出面板
        $handle.removeClass('is-active');
        $menu.slideUp('fast');
    });
};

/**
 * @description: 绑定清空
 * @param {*}
 * @return {*}
 * @Date: 2022-04-13 08:17:40
 */
Color.prototype._bindClear = function () {
    let that = this;
    $(document).on('click', this.CLEAR, function () {
        let $this = $(this),
            $container = $this.closest(that.CONTAINER),
            $binput = $this.closest(that.BOTTOM_BTNS).find('input'),
            $handle = $container.find(that.HANDLE),
            $input = $handle.find('input'),
            $close = $handle.find('i'),
            $colorShow = $container.find(that.COLOR_SHOW),
            $menu = $container.find(that.MENU);
        // 设置数据
        $binput.val('');
        $input.val('');
        $close.css('display', 'inline-block');
        $colorShow.css('background-color', 'transparent');
        // 关闭弹出面板
        $handle.removeClass('is-active');
        $menu.slideUp('fast');
    });
};

/**
 * @description: 绑定拖动
 * @param {*}
 * @return {*}
 * @Date: 2022-04-13 08:09:56
 */
Color.prototype._bindDrag = function () {
    let that = this;
    $(document).on('mousedown', this.SVPANEL, function () {
        that.markP = 'xy';
        that.$mark = $(that.SVPANEL_CURSOR);
        that.panelWidth = $(this).width();
        that.panelHeight = $(this).height();
        that.panelP = $(this).offset();
        // 启用拖动
        that._dragStart();
    });

    $(document).on('mousedown', this.HSLIDER, function () {
        that.markP = 'y';
        that.$mark = $(that.HSLIDER_THUMB);
        that.panelWidth = $(this).width();
        that.panelHeight = $(this).height();
        that.panelP = $(this).offset();
        // 启用拖动
        that._dragStart();
    });

    $(document).on('mousedown', this.ASLIDER, function () {
        that.markP = 'x';
        that.$mark = $(that.ASLIDER_THUMB);
        that.panelWidth = $(this).width();
        that.panelHeight = $(this).height();
        that.panelP = $(this).offset();
        // 启用拖动
        that._dragStart();
    });
};

/**
 * @description: 拖动开启(共用)
 * @param {*}
 * @return {*}
 * @Date: 2022-04-12 21:12:55
 */
Color.prototype._dragStart = function () {
    $(document).mousemove(this.dragMove.bind(this));

    $(document).mouseup(function () {
        $(document).off('mousemove', this.dragMove);
        return false;
    });
};

/**
 * @description: 拖动
 * @param {*} event
 * @return {*}
 * @Date: 2022-04-12 21:01:28
 */
Color.prototype.dragMove = function (event) {
    event.stopPropagation();
    event.preventDefault();
    let timer = null;
    if (!timer) {
        timer = setTimeout(() => {
            let x = event.pageX - this.panelP.left;
            let y = event.pageY - this.panelP.top;
            // 判断 x
            if (x < 0) {
                x = 0;
            } else if (x > this.panelWidth) {
                x = this.panelWidth;
            }
            // 判断 y
            if (y < 0) {
                y = 0;
            } else if (y > this.panelHeight) {
                y = this.panelHeight;
            }
            // 设置指示点
            if (this.markP === 'xy') {
                this.panelXY = [x, y];
            } else if (this.markP === 'x') {
                this.asliderX = x;
            } else if (this.markP === 'y') {
                this.hsliderY = y;
            }
            // 更新颜色
            this._updateColor({ x, y });
            // 更新指示的位置
            this._updateMarksPosition();
            // 清除定时器
            clearTimeout(timer);
            timer = null;
        }, 20);
    }
};

/**
 * @description: 更新颜色
 * @param {*} x
 * @param {*} y
 * @return {*}
 * @Date: 2022-04-12 23:44:11
 */
Color.prototype._updateColor = function ({ x, y }) {
    if (this.markP === 'xy') {
        this.hsv[1] = x / this.panelWidth;
        this.hsv[2] = (this.panelHeight - y) / this.panelHeight;
    } else if (this.markP === 'x') {
        this.alpha = parseInt((x / this.panelWidth) * 100) / 100;
    } else if (this.markP === 'y') {
        this.hsv[0] = parseInt((y / this.panelHeight) * 360);
        // 更新面板颜色
        this.svFieldHsv[0] = parseInt((y / this.panelHeight) * 360);
        this.svFieldRgb = this._hsv2rgb(parseInt(this.svFieldHsv[0]), this.svFieldHsv[1], this.svFieldHsv[2]);
        $(this.SVPANEL).css('background-color', `rgb(${this.svFieldRgb.join(',')})`);
        // 设置透明度条颜色
        $(this.ASLIDER_BAR).css('background', `linear-gradient(to right, rgba(${this.svFieldRgb.join()}, 0) 0%, rgb(${this.svFieldRgb.join()}) 100%)`);
    }
    // 将 hsv 转成 rgb 并保存
    this.rgb = this._hsv2rgb(this.hsv[0], this.hsv[1], this.hsv[2]);
    // 弹出框底部加入颜色值
    let $binput = $(this.BOTTOM_BTNS).find('input');
    if (this.isAlpha) {
        $binput.val(`rgba(${this.rgb.join()},${this.alpha})`);
    } else {
        $binput.val(`rgb(${this.rgb.join()})`);
    }
};

/**
 * @description: 更新指示的位置
 * @param {*}
 * @return {*}
 * @Date: 2022-04-12 23:43:50
 */
Color.prototype._updateMarksPosition = function () {
    $(this.SVPANEL_CURSOR).css({ left: this.panelXY[0], top: this.panelXY[1] });
    $(this.HSLIDER_THUMB).css('top', this.hsliderY);
    $(this.ASLIDER_THUMB).css('left', this.asliderX);
};

/**
 * @description: hsv 转 rgb
 * @param {*} h
 * @param {*} s
 * @param {*} v
 * @return {*}
 * @Date: 2022-04-13 08:11:00
 */
Color.prototype._hsv2rgb = function (h, s, v) {
    let hi = Math.floor(h / 60) % 6,
        f = h / 60 - Math.floor(h / 60),
        p = v * (1 - s),
        q = v * (1 - f * s),
        t = v * (1 - (1 - f) * s),
        c = [
            [v, t, p],
            [q, v, p],
            [p, v, t],
            [p, q, v],
            [t, p, v],
            [v, p, q],
        ][hi];
    return [parseInt(c[0] * 255), parseInt(c[1] * 255), parseInt(c[2] * 255)];
};

/**
 * @description: rgb 转 hsv
 * @param {*} r
 * @param {*} g
 * @param {*} b
 * @return {*}
 * @Date: 2022-04-13 08:11:13
 */
Color.prototype._rgb2hsv = function (r, g, b) {
    let max = Math.max(r, g, b),
        min = Math.min(r, g, b),
        h,
        s,
        v,
        d = max - min;
    if (max == min) {
        h = 0;
    } else if (max == r) {
        h = 60 * ((g - b) / d);
    } else if (max == g) {
        h = 60 * ((b - r) / d) + 120;
    } else {
        h = 60 * ((r - g) / d) + 240;
    }
    s = max == 0 ? 0 : 1 - min / max;
    v = max;
    if (h < 0) {
        h += 360;
    }
    return [h, s, v / 255];
};

let color = new Color();

export { color };
